package com.york.jobmew.subject.common.util;

/**
 * IdWorkerUtil 类是一个用于生成分布式唯一ID的工具类。
 * 它基于 Twitter 的 Snowflake 算法实现，能够在分布式系统中生成全局唯一的ID。
 * Snowflake 算法的核心思想是将一个64位的ID拆分成多个部分，分别表示时间戳、数据中心ID、机器ID和序列号，以确保生成的ID在分布式系统中是唯一的。
 */

public class IdWorkerUtil {

    // 机器ID，表示当前机器的唯一标识
    private long workerId;

    // 数据中心ID，表示当前数据中心的唯一标识
    private long datacenterId;

    // 序列号，用于同一毫秒内生成多个ID时进行自增
    private long sequence;

    // 起始时间戳，用于计算当前时间戳与起始时间戳的差值
    private long twepoch = 1585644268888L;

    // 机器ID的位数
    private long workerIdBits = 5L;

    // 数据中心ID的位数
    private long datacenterIdBits = 5L;

    // 序列号的位数
    private long sequenceBits = 12L;

    // 最大机器ID，根据机器ID的位数计算得出
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);

    // 最大数据中心ID，根据数据中心ID的位数计算得出
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);

    // 机器ID需要左移的位数，用于将机器ID拼接到最终的ID中
    private long workerIdShift = sequenceBits;

    // 数据中心ID需要左移的位数，用于将数据中心ID拼接到最终的ID中
    private long datacenterIdShift = sequenceBits + workerIdBits;

    // 时间戳需要左移的位数，用于将时间戳拼接到最终的ID中
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;

    // 序列号的掩码，用于限制序列号的最大值
    private long sequenceMask = -1L ^ (-1L << sequenceBits);

    // 上一次生成ID的时间戳，用于判断当前时间戳是否与上一次相同
    private long lastTimestamp = -1L;

    // 获取当前机器ID
    public long getWorkerId() {
        return workerId;
    }

    // 获取当前数据中心ID
    public long getDatacenterId() {
        return datacenterId;
    }

    // 获取当前时间戳
    public long getTimestamp() {
        return System.currentTimeMillis();
    }

    // 构造函数，初始化机器ID、数据中心ID和序列号
    public IdWorkerUtil(long workerId, long datacenterId, long sequence) {
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(
                    String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(
                    String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = sequence;
    }

    // 生成下一个唯一ID
    public synchronized long nextId() {
        long timestamp = timeGen();
        if (timestamp < lastTimestamp) {
            System.err.printf(
                    "clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
            throw new RuntimeException(
                    String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
                            lastTimestamp - timestamp));
        }

        // 如果当前时间戳与上一次生成ID的时间戳相同，则增加序列号
        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) & sequenceMask;
            // 如果序列号溢出，则等待下一毫秒
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            // 如果当前时间戳大于上一次生成ID的时间戳，则重置序列号
            sequence = 0;
        }
        lastTimestamp = timestamp;
        // 拼接时间戳、数据中心ID、机器ID和序列号，生成最终的ID
        return ((timestamp - twepoch) << timestampLeftShift) |
                (datacenterId << datacenterIdShift) |
                (workerId << workerIdShift) | sequence;
    }

    // 等待下一毫秒，直到当前时间戳大于上一次生成ID的时间戳
    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    // 获取当前时间戳
    private long timeGen() {
        return System.currentTimeMillis();
    }
}